#include "../include/parse.h"
#include "../include/minidbms.h"
#include "../include/DBMS.h"
stack<Sub_Stmt*> f;
//DBMS MyDB;//extern
void CallCreate(Create_Stmt);
void CallInsert(Insert_Stmt);
void CallSelect(Select_Stmt);
int GetType(string st)
{
    if (st=="NAME") return NAME;
    if (st=="TABLE") return TABLE;
    if (st=="FIELDNAME") return FIELDNAME;
    if (st=="ALIAS") return ALIAS;
    if (st=="NUMBER") return NUMBER;
    if (st=="STRING") return STRING;
    if (st=="CMP") return CMP;
    if (st=="CMPSELECT") return CMPSELECT;
    if (st=="AND") return AND;
    if (st=="OR") return OR;
    if (st=="WHERE") return WHERE;
    if (st=="ORDERBY") return ORDERBY;
    if (st=="GROUPBY") return GROUPBY;
    if (st=="DISTINCT") return DISTINCT;
    if (st=="LIKE") return LIKE;
    if (st=="JOIN") return JOIN;
    if (st=="ONEXPR") return ONEXPR;
    if (st=="SELECT") return SELECT;
    if (st=="SELECTALL") return SELECTALL;
    if (st=="COLUMN") return COLUMN;
    if (st=="INSERTCOLS") return INSERTCOLS;
    if (st=="VALUES") return VALUES;
    if (st=="INSERTVALS") return INSERTVALS;
    if (st=="ASSIGN") return ASSIGN;
    if (st=="UPDATE") return UPDATE;
    if (st=="DELETEONE") return DELETEONE;
    if (st=="STARTCOL") return STARTCOL;
    if (st=="COLUMNDEF") return COLUMNDEF;
    if (st=="ATTR") return ATTR;
    if (st=="PRIKEY") return PRIKEY;
    if (st=="DEFAULT") return DEFAULT;
    if (st=="CREATE") return CREATE;
    if (st=="CREATEDATABASE") return CREATEDATABASE;
    if (st=="USEDATABASE") return USEDATABASE;
    if (st=="STMT") return STMT;
    return -1;
}
void Process(int type);
ifstream fin;
/*void parse()
{

    system("./Parser correct");
    fin.open("Processed.tmp");
    if(!fin)
    {
        printf("Cannot open process file. Please check and try again.");
        return ;
    }

    string st;
    while (fin>>st)
    {
        if (st=="SQL") break;
        fin>>st;
        cout << st<<endl;
        int type=GetType(st);
        if (type!=-1)
        {
            Process(type);
        }
        else
        {
            cout<<st<<endl;
            cout<<"Unexpected Command!"<<endl;
            break;
        }
    }

    return ;

}
*/

void parse(string name)
{
    fin.open(name.c_str());
    if(!fin)
    {
        printf("Cannot open SQL file. Please check and try again.");
        return ;
    }

    string st;
    while (fin>>st)
    {
        if(st.find(";")==st.npos)
        {
            string temp;
            transform(st.begin(),st.end(),st.begin(),(int (*)(int))tolower);
            do
            {
                if(fin >> temp)
                {
                    st = st + " "+ temp;
                }
                else
                {
                    st = st + ";";
                    break;
                }
            }while(temp.find(";")==temp.npos);
        }//read until meet ";" or eof

        regex reg_select("select\\s*(([a-z0-9_\\*\\.]+\\s*,?\\s*)+|count\\s*\\(\\s*([a-z0-9_\\*\\.]+)\\s*\\)|sum\\s*\\(\\s*([a-z0-9_\\*\\.]+)\\s*\\))\\s*from\\s*(([a-z0-9_]+)\\s*(as\\s*[a-z0-9_]+)?)(\\s*,\\s*([a-z0-9_]+)\\s*(as\\s*[a-z0-9_]+)?)*\\s*(where\\s*([a-z0-9_\\*\\.]+)\\s*([<>=]+)\\s*(\'[^\']+\'|[a-z0-9_\\.]+)\\s*((and|or)\\s*([a-z0-9_\\*\\.\\s]+)\\s*([<>=]+)\\s*(\'[^\']+\'|[a-z0-9_\\.]+))?)?\\s*;",regex::icase);
        regex reg_create("^create table ([a-z0-9_]*)\\s*\\((\\s*([a-z0-9_]*)\\s+(int|varchar\\(\\d+\\))\\s*(primary\\s+key)?[,]?)+\\s*\\)\\s*;",regex::icase);
        regex reg_insert("^insert\\s*into\\s*([a-z0-9_]*)\\s*(\\(([a-z0-9_]\\s*,?\\s*)+\\))?\\s*values\\s*\\(((([0-9\\-])|\'([^\']*)\')\\s*,?\\s*)*\\)\\s*;",regex::icase);

        cmatch what;
        bool ismatch = false;
        if (regex_match(st.c_str(),what, reg_create))
        {// This is a create statement
            ismatch = true;
            smatch what2;
            std::cout << "This is a correct create statement" << std::endl;
            Create_Stmt* u=new(Create_Stmt);
            u->type=CREATE;
            u->table = string(what[1]);
            u->Column.clear();
            //get attributes
            regex reg_attribute("([a-z0-9_]*)\\s+(int|varchar\\(\\d+\\))\\s*(primary\\s+key)?",regex::icase);
            std::string::const_iterator start = st.begin();
            std::string::const_iterator end = st.end();
            while( boost::regex_search(start, end, what2, reg_attribute) )
            {
                Columndef_Stmt * v = new(Columndef_Stmt);
                v->type = COLUMNDEF;
                v->Name = string(what2[1]);
                regex reg_varchar("varchar\\s*\\((\\d+)\\)");
                if(regex_match(string(what2[2]).c_str(),what,reg_varchar))
                {
                    v->ColumnType = Varchar;
                    v->Size = atoi(string(what[1]).c_str());
                }
                else
                    v->ColumnType = Int;
                if(what2[3] == "")
                    v->IsPrimary = false;
                else
                    v->IsPrimary = true;
                u->Column.push_back(v);
                start = what2[0].second;
            }
            CallCreate(*u);
        }
        if(regex_match(st.c_str(),what, reg_insert))
        {//This is an insert statement
            //std::cout << "This is a correct insert statement" << std::endl;
            ismatch = true;
            Insert_Stmt* u=new(Insert_Stmt);
            u->type=INSERTVALS;
            u->table = string(what[1]);
            smatch what2;
            regex reg_haveNameList("^insert\\s*into\\s*[a-z0-9_]+\\s*\\(",regex::icase);
            u->AnyName=false;
            std::string::const_iterator start = st.begin();
            std::string::const_iterator end = st.end();
            //get name list
            if(regex_search(start, end, what2, reg_haveNameList))
            {
                regex_search(start, end, what2, regex("\\s+values\\s+",regex::icase));
                end = what2[0].second;
                u->AnyName = true;
                ColumnName_Stmt* v = new(ColumnName_Stmt);
                v->Columnname.clear();
                regex reg_name("([a-z0-9_]+)\\s*(,|\\))",regex::icase);


                while( boost::regex_search(start, end, what2, reg_name) )
                {
                    u->Name.Columnname.push_back(string(what2[1]));
                    //cout << what2[1]<<endl;
                    start = what2[0].second;
                }
            }
            end = st.end();
            regex_search(start, end, what2, regex("\\s+values\\s+",regex::icase));
            start = what2[0].second;
            //get values
            regex reg_values("([0-9]+)|\'([^\']*)\'",regex::icase);
            while( boost::regex_search(start, end, what2, reg_values) )
            {
                if(what2[1]=="")
                {// This is a varchar
                    String_Stmt *v = new(String_Stmt);
                    v->type = STRING;
                    v->str = what2[2];
                    u->Value.value.push_back((Sub_Stmt*)v);
                }
                else
                {
                    Number_Stmt *v = new(Number_Stmt);
                    v->type = NUMBER;
                    v->num = atoi(string(what2[1]).c_str());
                    u->Value.value.push_back((Sub_Stmt*)v);
                }
                start = what2[0].second;
            }
            //cout<<ccc++<<endl;
            CallInsert(*u);

        }
        regex reg_select1("select\\s*(count\\s*\\(\\s*([a-z0-9_\\*\\.]+)\\s*\\)|sum\\s*\\(\\s*([a-z0-9_\\*\\.]+)\\s*\\)|([a-z0-9_\\*\\.]+\\s*(,|\\s+))+)\\s*",regex::icase);
        regex reg_select2("\\s*(([a-z0-9_]+)\\s*(as\\s*[a-z0-9_]+)?)(\\s*,\\s*([a-z0-9_]+)\\s*(as\\s*[a-z0-9_]+)?)*\\s*(where\\s*([a-z0-9_\\*\\.]+)\\s*([<>=]+)\\s*(\'[^\']+\'|[a-z0-9_\\.]+)\\s*((and|or)\\s*([a-z0-9_\\*\\.\\s]+)\\s*([<>=]+)\\s*(\'[^\']+\'|[a-z0-9_\\.]+))?)?\\s*",regex::icase);
        smatch what2;
        std::string::const_iterator start = st.begin();
        std::string::const_iterator end = st.end();
        if(st.find("select")!=st.npos)
        {//This is an insert statement
            regex reg_splitSelect("select\\s+(.*)\\s+from\\s+(.*)\\s*;",regex::icase);
            regex_search(start,end,what2,reg_splitSelect);
            //cout << what2[1] << endl<< what2[2]<<endl << what2[3] <<endl<<what2[4]<< endl;
            string attributes = what2[1];
            string tablesAndConditions = what2[2];
            start =tablesAndConditions.begin();
            end = tablesAndConditions.end();
            ismatch = true;
            if(regex_search(start,end,what2,reg_select2))
            {
                Select_Stmt* u=new(Select_Stmt);
                u->type=SELECT;
                start = st.begin();
                regex_search(start,end,what2, reg_select1);
                if(what2[4]!="")
                {//This is a select without function
                    //cout << what2[4] << endl;
                    u->SelectType = 0;
                    regex reg_attribute("(\\s+|\\s*,\\s*)([a-z0-9_\\.\\*]*)\\s*",regex::icase);
                    start = st.begin();
                    end = st.end();
                    boost::regex_search(start, end, what2, regex("\\s+from\\s+",regex::icase));
                    end = what2[0].second;
                    while( boost::regex_search(start, end, what2, reg_attribute) )
                    {
                        u->AttrName.push_back(string(what2[2]));
                        //cout << what2[2]<<endl;
                        start = what2[0].second;
                    }
                }
                else if(what2[2]!="")
                {// this is a select with count function
                    u->SelectType = 1;
                    u->AttrName.push_back(what2[2]);// only one column can in function variable.
                }
                else if(what2[3]!="")
                {// this is a select with sum function
                    u->SelectType = 2;
                    u->AttrName.push_back(what2[3]);// only one column can in function variable.
                }
                else{
                    cout << "Here is an Error in Select Statement Before From" << endl;
                    continue;
                }
                //get table name and establish originname KVDB
                regex reg_getfrom("\\s+from\\s+",regex::icase);
                regex reg_getwhere("\\s+where\\s+",regex::icase);
                start = st.begin();
                end = st.end();
                regex_search(start, end, what2, reg_getfrom);
                if(what2[0]=="")
                {
                    cout << "select without from, please check." << endl;
                    continue;
                }
                start = what2[0].second;
                regex_search(start, end, what2, reg_getwhere);
                bool haswhere = false;
                if(what2[0]!="")
                {
                    end = what2[0].second;
                    haswhere = true;
                }
                regex reg_getTableName("([a-z0-9_]+)\\s*(as\\s*([a-z0-9_]+))?",regex::icase);
                while( boost::regex_search(start, end, what2, reg_getTableName) )
                {
                    if(what2[3]!="")
                    {//there is an as statement
                        u->originname[string(what2[3])] = string(what2[1]);
                        u->TableName.push_back(string(what2[3]));
                    }
                    else//no as
                        u->TableName.push_back(string(what2[1]));
                    start = what2[0].second;
                }
                end = st.end();
                //get conditions
                if(haswhere)
                {
                    //cout << "here is a where condition" << endl;
                    regex reg_condition("([a-z0-9_\\*\\.]+)\\s*([<>=]+)\\s*(\'[^\']+\'|[a-z0-9_\\.]+)",regex::icase);
                    while( boost::regex_search(start, end, what2, reg_condition) )
                    {
                        //cout << "here is a condition expression" << endl;
                        cmatch what3;
                        start = what2[0].second;
                        regex reg_split("([a-z0-9_]+)\\.([a-z0-9_]+)",regex::icase);
                        Condition_Stmt * w = new(Condition_Stmt);
                        w->type = CONDITION;
                        w->cond1.table.clear();
                        w->cond1.type = ALIAS;
                        string temp = what2[1];

                        if(regex_match(temp.c_str(),what3,reg_split))
                        {//需要分割
                            w->cond1.table = what3[1];
                            w->cond1.attr = what3[2];
                        }
                        else
                            w->cond1.attr = what2[1];
                        if(what2[2]==">")
                            w->symbol = 0;
                        if(what2[2]=="<")
                            w->symbol = 1;
                        if(what2[2]=="=")
                            w->symbol = 2;
                        if(what2[2]=="<>")
                            w->symbol = 3;
                        temp = what2[3];
                        if(regex_match(temp.c_str(),what3,reg_split))
                        {//需要分割
                            Alias_Stmt *v = new(Alias_Stmt);
                            v->type = ALIAS;
                            v->table = what3[1];
                            v->attr = what3[2];
                            w->cond2 = v;
                        }
                        else
                        {
                            if(regex_match(temp.c_str(),what3,regex("[0-9]+")))
                            {
                                Number_Stmt *v = new(Number_Stmt);
                                v->type = NUMBER;
                                v->num = atoi(temp.c_str());
                                w->cond2 = v;
                            }
                            else if(regex_match(temp.c_str(),what3,regex("\'([^\']+)\'")))
                            {
                                String_Stmt *v = new(String_Stmt);
                                v->type = STRING;
                                v->str = what3[1];
                                w->cond2 = v;
                            }
                            else
                            {
                                cout << "condition expression error" << endl;
                            }
                        }
                        u->WhereCond.push_back(w);
                    }
                    //get and/or
                    start = st.begin();
                    while( boost::regex_search(start, end, what2, regex("\\s+((and)|(or))\\s+",regex::icase)) )
                    {
                        if(what2[3]!="")//or
                            u->Relation.push_back(1);
                        else
                             u->Relation.push_back(0);


                        start = what2[0].second;
                    }
                }
                CallSelect(*u);

            }
        }
        if(!ismatch)
        {
            cout << "Not a correct statement." << endl;
        }
    }

    return ;

}
void Process(int type)
{
    if (type==NAME)
    {
        Name_Stmt* u=new(Name_Stmt);
        u->type=NAME;
        u->name.clear();
        u->Alias.clear();
        u->AnyAlias=false;
        u->IsAll=false;
        fin>>u->name;
        Sub_Stmt* v=u;
        f.push(v);
    }

    if (type==TABLE)
    {
        Table_Stmt* u=new(Table_Stmt);
        u->type=TABLE;
        fin>>u->table;
        Sub_Stmt* v=u;
        f.push(v);
    }

    if (type==FIELDNAME)
    {
        FieldName_Stmt* u=new(FieldName_Stmt);
        u->type=FIELDNAME;
        string str;
        fin>>str;
        unsigned i,j;
        for (i=0; i<str.size(); ++i)
            if (str[i]!='.') u->table+=str[i];
            else break;
        for (j=i+1; j<str.size(); ++j)
            u->name+=str[j];
        Sub_Stmt* v=u;
        f.push(v);
    }


    if (type==NUMBER)
    {
        Number_Stmt* u=new(Number_Stmt);
        u->type=NUMBER;
        fin>>u->num;
        Sub_Stmt* v=u;
        f.push(v);
    }

    if (type==STRING)
    {
        String_Stmt* u=new(String_Stmt);
        u->type=STRING;
        u->str.clear();
        char ch;
        while(1)
        {
            fin >> ch;
            if(ch == '\'')
                break;
        }
        string tmp;
        getline(fin,tmp);
        //fin >> tmp;
        tmp.erase(tmp.size()-1,1);
        //cout<<tmp<<endl;
        u->str = tmp;
        Sub_Stmt* v=u;
        f.push(v);
    }
    if (type==ONEXPR)
    {
    }


    if (type==VALUES)
    {
        Values_Stmt* u=new(Values_Stmt);
        u->type=VALUES;
        u->value.clear();

        int r;
        fin>>r;

        while (r--)
        {
            Sub_Stmt* v=f.top();
            f.pop();
            u->value.push_front(v);

        }
        Sub_Stmt* w;
        w=(Sub_Stmt*)u;
        f.push(w);
    }

    if (type==COLUMN)
    {
        Column_Stmt* u=new(Column_Stmt);
        u->type=COLUMN;
        fin>>u->Columnname;

        Sub_Stmt* v;
        v=(Sub_Stmt*)u;
        f.push(v);
    }

    if (type==INSERTCOLS)
    {
        ColumnName_Stmt* u=new(ColumnName_Stmt);
        u->type=INSERTCOLS;
        u->Columnname.clear();
        int r;
        fin>>r;
        while (r--)
        {
            Column_Stmt* v=(Column_Stmt*)(f.top());
            f.pop();
            u->Columnname.push_front(v->Columnname);
        }
        Sub_Stmt* w;
        w=(Sub_Stmt*)u;
        f.push(w);
    }

    if (type==INSERTVALS)
    {
        Insert_Stmt* u=new(Insert_Stmt);
        u->type=INSERTVALS;
        u->AnyName=false;
        int r,s;
        fin>>r>>s>>u->table;
        if  ((!f.empty()) && (f.top()->type==VALUES))
        {
            Values_Stmt* v=(Values_Stmt*)(f.top());
            f.pop();
            u->Value=*v;
        }
        if ((!f.empty()) && (f.top()->type==INSERTCOLS))
        {
            ColumnName_Stmt* v=(ColumnName_Stmt*)(f.top());
            f.pop();
            u->Name=*v;
            u->AnyName=true;
        }
        Sub_Stmt* w;
        w=(Sub_Stmt*)u;
        f.push(w);
        CallInsert(*((Insert_Stmt*)(f.top())));
        f.pop();
    }


    if (type==STARTCOL)
    {
        Columndef_Stmt* u;
        Columndef_Stmt* v;
        Sub_Stmt* r;
        string st;
        fin>>st;
        fin>>st;
        int type=GetType(st);
        bool isPrimary = false;
        if( type == ATTR)
        {
            fin>>st;
            if(GetType(st) == PRIKEY)
            {
                isPrimary = true;
            }
            fin>>st;
            fin>>st;
            type = GetType(st);
        }
        if (type==COLUMNDEF)
        {
            int t;
            fin>>t;
            if (t==50000)
            {
                u=new(Columndef_Stmt);
                u->type=COLUMNDEF;
                u->ColumnType=Int;
                u->IsPrimary = isPrimary;
                fin>>u->Name;
                r=(Sub_Stmt*) u;
            }
            else if(t > 130000)
            {
                v=new(Columndef_Stmt);
                v->type=COLUMNDEF;
                v->ColumnType=Varchar;
                v->Size = t % 100;
                v->IsPrimary = isPrimary;
                fin>>v->Name;
                r=(Sub_Stmt*) v;
            }
            else{
                cout << "Data Type doesn't supported"<<endl;
                exit(0);
            }
        }

        f.push(r);
    }

    if (type==CREATE)
    {
        Create_Stmt* u=new(Create_Stmt);
        u->type=CREATE;
        int r,s,t;
        fin>>r>>s>>t;
        fin>>u->table;
        u->Column.clear();
        while (t--)
        {
            Columndef_Stmt* v=(Columndef_Stmt*)f.top();
            f.pop();
            u->Column.push_front(v);
        }
        Sub_Stmt* w;
        w=(Sub_Stmt*) u;
        f.push(w);

        CallCreate(*((Create_Stmt*)(f.top())));
        f.pop();
    }


    if (type==STMT)
    {
    }

    return ;
}
